﻿using IntoTheVoid.State;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Shapes;

namespace IntoTheVoid.Units.Download
{
	public class DownloadService
	{
		private string downloadUrl = "";    // 文件下载地址
		private string filePath = "";       // 文件保存路径
		private string method = "GET";         // 方法
		private long fromIndex = 0;         // 开始下载的位置
		private long toIndex = 0;           // 结束下载的位置
		private long count = 0;             // 总大小
		public long size = 10000000;        // 每次下载大小
		private bool isRun = false;         // 是否正在进行
		TaskInfo infoTask = null;
		private int writeLogCount = 0;
		public bool isFinish { get; private set; } = false; //是否已下载完成
		public bool isStopped { get; private set; } = true; //是否已停止


		public event Action OnStart;
		public event Action OnDownload;
		public event Action OnFinsh;
		public event Action<string> OnError;
		public Task task;
		public void SetSpeed(long speed)
		{
			if (speed < 0)
				return;
			size = speed;
		}
		public long GetTotalCount()
		{
			return count;
		}
		public long GetDownloadedCount()
		{
			return count - toIndex + fromIndex - 1;
		}

		public void Stop()
		{
			isRun = false;
		}
		private const int MaxRetries = 3;
		private int currentRetry = 0;

		public bool Start(TaskInfo info, bool isReStart)
		{
			if (task != null)
				return true;
			infoTask = info;
			downloadUrl = info.downloadUrl;
			fromIndex = info.fromIndex;
			toIndex = info.toIndex;
			method = info.method;
			filePath = info.filePath;
			count = info.count;
			isStopped = false;

			if (File.Exists(filePath))
			{
				if (isReStart)
				{
					File.Delete(filePath);
					File.Create(filePath).Close();
				}
			}
			else
			{
				File.Create(filePath).Close();
			}
			try
			{
				using (var file = File.Open(filePath, FileMode.Open))
				{
					fromIndex = info.fromIndex + file.Length;
				}
				using (var file = File.Open(filePath, FileMode.Open))
				{
					fromIndex = info.fromIndex + file.Length;
				}
			}
			catch (Exception ex)
			{
				LogHelper.Error(ex, "DownLoad Start Error");
				return false;
			}

			if (fromIndex >= toIndex)
			{
				OnFinishHandler();
				isFinish = true;
				isStopped = true;
				return false;
			}
			OnStartHandler();
			isRun = true;
			task = Task.Run(() =>
			{
				try
				{
					WebResponse rsp;
					var timePre = Environment.TickCount;
					var timeNow = Environment.TickCount;
					while (fromIndex < toIndex && isRun && currentRetry < MaxRetries)
					{
						timeNow = Environment.TickCount;
						if (timeNow - timePre <= 1000)
						{
							continue;
						}
						timePre = timeNow;
						long to;
						if (fromIndex + size >= toIndex - 1)
							to = toIndex - 1;
						else
							to = fromIndex + size;
						using (rsp = DownloadHttpHelper.Download(downloadUrl, fromIndex, to, method))
						{
							Save(filePath, rsp.GetResponseStream());
						}
					}
				}
				catch (WebException ex)
				{
					currentRetry++;
					Thread.Sleep(1000 * currentRetry); // 指数退避
					Start(info, false); // 非递归重启
				}
				catch (Exception ex)
				{
					writeLogCount++;
					if (writeLogCount < 30)
						LogHelper.Error(ex, "DownLoad Start Error");
					task = null;
					// 异常不影响下载
					// OnErrorHandler();
				}
				finally
				{
					Start(info, false);
					currentRetry = 0;
				}

				if (!isRun)
					isStopped = true;
				if (fromIndex >= toIndex)
				{
					isFinish = true;
					isStopped = true;
					OnFinishHandler();
					task = null;
				}
			});
			return true;
		}

		private void Save(string filePath, Stream stream)
		{
			const int maxRetries = 3;
			int retryCount = 0;
			byte[] buffer = new byte[8192];

			try
			{
				using (var writer = new FileStream(filePath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
				{
				
					writer.Seek(0, SeekOrigin.End);

					while (isRun && retryCount <= maxRetries)
					{
						int length;
						try
						{
							length = stream.Read(buffer, 0, buffer.Length);
							if (length == 0) break;
							retryCount = 0;
						}
						catch (IOException ex) when (ex.InnerException is SocketException)
						{
							if (++retryCount > maxRetries) 
								throw;
							LogHelper.Warn($"网络中断重试 [{retryCount}/{maxRetries}]");
							Thread.Sleep(1000 * retryCount);
							continue;
						}

						writer.Write(buffer, 0, length);
						fromIndex += length;

						OnDownloadHandler();
					}
					writer.Flush();
				}
				OnDownloadHandler();
			}
			catch (Exception e)
			{
				writeLogCount++;
				if (writeLogCount < 30)
				{
					LogHelper.Error(e, $"下载流保存失败 [已重试{retryCount}次]");
				}
				if (e is IOException && retryCount <= maxRetries)
				{
					Thread.Sleep(2000);
					throw e;
				}
				throw;
			}
			finally
			{
				stream?.Dispose();
			}
		}

		private void OnErrorHandler(string message)
		{
			Task.Run(() =>
			{
				OnError?.Invoke(message);
			});
		}
		private void OnStartHandler()
		{
			Task.Run(() =>
			{
				OnStart?.Invoke();
			});
		}
		private void OnFinishHandler()
		{
			Task.Run(() =>
			{
				OnFinsh?.Invoke();
				OnDownload?.Invoke();
			});
		}
		private void OnDownloadHandler()
		{
			Task.Run(() =>
			{
				OnDownload?.Invoke();
			});
		}
	}

}
